﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Framework.Event;
using Framework.Infrastructure;
using Framework.Config;
using Microsoft.Practices.Unity;
using System.Messaging;

namespace Framework.Bus
{
    class AsyncBusImpl<TMessage> : IAsyncBus<TMessage>
        where TMessage : IMessage
    {
        #region MSMQ
        private readonly Dictionary<string, MsmqHandler> MSMessageQueues;
        #endregion

        public AsyncBusImpl()
        {
            var endpointsMappings = ConfigurationReader.Instance
                                .GetConfigurationSection<FrameworkConfigurationSection>()
                                .MessageEndpointMappings;

            MSMessageQueues = new Dictionary<string, MsmqHandler>();

            foreach (EndpointElement endpointMapping in endpointsMappings)
            {
                try
                {
                    if (Type.GetType(endpointMapping.MessageType).IsAssignableFrom(typeof(TMessage)))
                    {
                        var msmq = new MessageQueue(endpointMapping.Endpoint, false, false, QueueAccessMode.SendAndReceive);
                        msmq.Formatter = new BinaryMessageFormatter(System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full,
                        System.Runtime.Serialization.Formatters.FormatterTypeStyle.TypesAlways);
                        MSMessageQueues.Add(endpointMapping.Assembly, new MsmqHandler { Msmq = msmq, EndpointElement = endpointMapping });
                    }
                }
                catch (System.Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }


        protected virtual void SendMSMQMessage(TMessage message, MessageQueue msmq, MessageQueueTransaction transaction = null)
        {
            Message msmqMessage = new Message(message);
            msmqMessage.Recoverable = true;
            msmqMessage.Formatter = msmq.Formatter;
            if (transaction != null)
            {
                msmq.Send(msmqMessage, transaction);
            }
            else
            {
                msmq.Send(msmqMessage, MessageQueueTransactionType.Automatic);
            }
        }



        #region IBus<IMessage> Members

        public void Commit()
        {
            throw new NotImplementedException();
        }

        public void Publish(TMessage message)
        {
            lock (this)
            {
                MsmqHandler msmqHandler = null;
                if (MSMessageQueues.TryGetValue(message.GetType().Assembly.GetName().Name, out msmqHandler))
                {
                    if (msmqHandler.EndpointElement.UseInternalTransaction)
                    {
                        using (MessageQueueTransaction transaction = new MessageQueueTransaction())
                        {
                            transaction.Begin();
                            SendMSMQMessage(message, msmqHandler.Msmq, transaction);
                            transaction.Commit();
                        }
                    }
                    else
                    {
                        SendMSMQMessage(message, msmqHandler.Msmq);
                    }
                }
            }
        }

        public void Publish(IEnumerable<TMessage> messages)
        {
            lock (this)
            {
                using (MessageQueueTransaction transaction = new MessageQueueTransaction())
                {
                    transaction.Begin();
                    messages.ForEach(message =>
                    {
                        MsmqHandler msmqHandler = null;
                        if (MSMessageQueues.TryGetValue(message.GetType().Assembly.GetName().Name, out msmqHandler))
                        {
                            if (msmqHandler.EndpointElement.UseInternalTransaction)
                            {
                                SendMSMQMessage(message, msmqHandler.Msmq, transaction);
                            }
                            else
                            {
                                SendMSMQMessage(message, msmqHandler.Msmq);
                            }
                        }
                    });
                    transaction.Commit();
                }
            }
        }


        public void Send(TMessage message, Action action)
        {
            throw new NotImplementedException();
        }

        public void Send(TMessage message)
        {
            Publish(message);
        }

        public void Send<TResult>(TMessage message, Action<TResult> action = null)
        {
            throw new NotImplementedException();
        }

        public void Send(IEnumerable<TMessage> messages)
        {
            Publish(messages);
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
        }

        #endregion
    }
}
